/**
 * @(#)PursuitClient.java
 *
 *
 * @author Duyun Chen
 * @version 1.00 2008/10/2
 */

package Pursuit_Client;

import java.io.*;
import java.util.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import Pursuit_Server.*;

public class PursuitClient extends JFrame implements ActionListener, ItemListener, FocusListener 
{
	private static final long serialVersionUID = -4941580527652766479L;
	private JPanel content;
	private static JScrollPane graphics_scrollpane;
	private PursuitGraphicsPanel2 graphics_panel;
	private PursuitAlgorithmListPanel algorithmPanel;
	private JButton connect_button;
	private static JTextArea console;
    private JTextField serverIPField = null;
    private JTextField serverPortField = null;
    private JTextField playerNameField = null;
    private JTextField gameNameField = null;
    private JCheckBox observerModeCheckBox = null;
    private JCheckBox logCheckBox = null;
	
	private PursuitGame game;
	private PursuitServer server;
	private PursuitDebugger debug;
	private static PursuitLog log;
	
	private static boolean isLogging = false;
	
	public PursuitClient(int x, int y) 
	{
    	super("Pursuit Client - BETA Duyun Chen");
    	setLocation(x,y);
    	setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	setResizable(false);
    	setFocusable(true);
    	
    	game = new PursuitGame(this); //create game engine class
    	log = new PursuitLog();	//create logger
    	debug = new PursuitDebugger();	//create debugger
    	graphics_panel = new PursuitGraphicsPanel2(this); //create graphics component
    	graphics_panel.begin();	//start the graphics loop
    	graphics_scrollpane = new JScrollPane(graphics_panel,
											JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
    										JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

    	
    	String[] opts = {"--debug"};
    	server = new PursuitServer(opts);	//create server

    	debug.addField("My X");
    	debug.addField("My Y");    	
    	debug.addField("My Theta");
    	debug.addField("My U");
    	debug.addField("My V");
    	debug.addField("My Omega");
    	
    	debug.addField("His X");
    	debug.addField("His Y");    	
    	debug.addField("His Theta");
    	debug.addField("His U");
    	debug.addField("His V");
    	debug.addField("His Omega");
    	
    	content = createContentPanel();
    	setJMenuBar(createMenu());
	
    	setContentPane(content);
    	
    	pack();
    	toFront();
    	setVisible(true);
	}
	
    public PursuitClient() 
    {
    	this(0,0);
    }
    
    private JPanel createContentPanel() 
    {
    	JPanel p = new JPanel();
    	//p.setPreferredSize(new Dimension(550,600));
    	p.setLayout(new GridBagLayout());
    	GridBagConstraints cc = new GridBagConstraints();
    	
    	//create console textarea and contained scrollpane
    	console = new JTextArea("[CONSOLE]\n");
    	console.setEditable(false);
    	console.setFont( new Font( "Dialog", Font.BOLD, 12 ) );
    	console.setLineWrap(true);
    	console.setWrapStyleWord(true);
    	JScrollPane console_scrollpane = new JScrollPane(console, 
    		JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 
    		JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    	console_scrollpane.setPreferredSize(new Dimension(400,100));
    	console_scrollpane.setBorder(BorderFactory.createLoweredBevelBorder());
    	
    	//create Panel for settings
    	JPanel SettingsPanel = new JPanel();
    	SettingsPanel.setLayout(new GridBagLayout());
    	GridBagConstraints c = new GridBagConstraints();
    	SettingsPanel.setBorder(BorderFactory.createLoweredBevelBorder());
    	    	
    	//create button "Connect"
    	connect_button = new JButton("CONNECT!");
    	connect_button.addActionListener(this);
    	connect_button.setBackground(Color.green);
    	connect_button.setForeground(Color.white);
    	connect_button.setBorder(BorderFactory.createLoweredBevelBorder());
    	
    	//create Settings fields and labels
    	JLabel ServerIPLabel = new JLabel("Server IP: ");
    	JLabel ServerPortLabel = new JLabel("Server Port: ");
    	JLabel PlayerNameLabel = new JLabel("Player Name: ");
    	JLabel GameNameLabel = new JLabel("Game Name:");
    	serverIPField = new JTextField("127.0.0.1", 10);
    	serverPortField = new JTextField("1337", 10);
    	playerNameField = new JTextField("vokuheila", 10);
     	gameNameField = new JTextField("test",10);
     	observerModeCheckBox = new JCheckBox("Observer");
     	logCheckBox = new JCheckBox("Log Output");
     	serverIPField.addFocusListener(this);
     	serverPortField.addFocusListener(this);
     	playerNameField.addFocusListener(this);
     	gameNameField.addFocusListener(this);
    	ServerIPLabel.setLabelFor(serverIPField);
    	ServerPortLabel.setLabelFor(serverPortField);
    	GameNameLabel.setLabelFor(gameNameField);
    	PlayerNameLabel.setLabelFor(playerNameField);
    	observerModeCheckBox.addItemListener(this);
    	logCheckBox.addItemListener(this);

    	//do layout for settings panel
    	c.gridx = 0; c.gridy = 0; c.fill = GridBagConstraints.BOTH;
    	SettingsPanel.add(ServerIPLabel, c);    	
    	c.gridx = 1; c.gridy = 0;
    	SettingsPanel.add(serverIPField, c);
    	c.gridx = 0; c.gridy = 1;
    	SettingsPanel.add(ServerPortLabel, c);
    	c.gridx = 1; c.gridy = 1;
    	SettingsPanel.add(serverPortField, c);
		c.gridx = 0; c.gridy = 2;
		SettingsPanel.add(PlayerNameLabel, c);
    	c.gridx = 1; c.gridy = 2;
    	SettingsPanel.add(playerNameField, c);
		c.gridx = 0; c.gridy = 3;
		SettingsPanel.add(GameNameLabel, c);
    	c.gridx = 1; c.gridy = 3;
    	SettingsPanel.add(gameNameField, c);
    	c.gridx = 0; c.gridy = 4;
    	SettingsPanel.add(observerModeCheckBox, c);
    	c.gridx = 1; c.gridy = 4;
    	SettingsPanel.add(logCheckBox, c);
    	
    	//create label for the AlgorithmListPanel
    	JLabel AlgorithmListLabel = new JLabel("Available Algorithms:");
    	AlgorithmListLabel.setLabelFor(algorithmPanel);
    	AlgorithmListLabel.setBorder(BorderFactory.createLoweredBevelBorder());    	
    	
    	//do layout for general GUI
    	cc.gridx = 1; cc.gridy = 0; cc.gridheight = 1; cc.fill = GridBagConstraints.BOTH;
    	p.add(AlgorithmListLabel, cc);
    	cc.gridx = 1; cc.gridy = 1;
    	p.add(algorithmPanel = new PursuitAlgorithmListPanel(this), cc);   
    	cc.gridx = 1; cc.gridy = 2;
    	p.add(connect_button, cc);    	
    	cc.gridx = 1; cc.gridy = 3;
    	p.add(SettingsPanel, cc);
    	cc.gridx = 0; cc.gridy = 0; cc.gridheight = 2; 
    	p.add(graphics_scrollpane, cc);    	
    	cc.gridx = 0; cc.gridy = 2; cc.gridheight = 2;
    	p.add(console_scrollpane, cc);
    	
    	return p;
    }
    
    private JMenuBar createMenu() 
    {
    	JMenuBar m = new JMenuBar();
    	JMenu file_menu = new JMenu("File");
    	JMenuItem connect = new JMenuItem("Connect");
    	JMenuItem startserver = new JMenuItem("Start Server");
    	JMenuItem exit = new JMenuItem("Exit");
    	JMenu debug_menu = new JMenu("Debug");
    	JMenu server_menu = new JMenu("Server");
    	JMenuItem launch = new JMenuItem("Launch");
    	connect.addActionListener(this);
    	startserver.addActionListener(this);
    	launch.addActionListener(this);
    	exit.addActionListener(this);
    	file_menu.add(connect);
    	server_menu.add(startserver);
    	file_menu.add(exit);
    	debug_menu.add(launch);
    	m.add(file_menu);
    	m.add(server_menu);
    	m.add(debug_menu);
    	
    	//print("Menu created");
    	return m;
    }
    
    //Handles all user interactions
    public void actionPerformed(ActionEvent e) 
    {
    	Object o = e.getSource();
    	//Handle menu items
    	if (o instanceof JMenuItem) 
    	{
    		JMenuItem source = (JMenuItem) o;
    		if (source.getText().equals("Connect")) 
    		{
    			source.setText("Disconnect");
				//game.start();
    		}
    		else if (source.getText().equals("Start Server")) {
    			source.setEnabled(false);
    			setTitle(getTitle() + " (Server started)");
    			server.start();
    			print("Server started");
    		}
    		else if (source.getText().equals("Exit")) 
    			System.exit(1);
    		else if (source.getText().equals("Launch")) {
    			debug.setVisible(true);
    			print("Debug window launched");
    		}
    	}
    	//Handle buttons
    	else if (o instanceof JButton) 
    	{
    		JButton source = (JButton) o;
    		if (source.getText().equals("CONNECT!")) 
    		{
    			//do a final sweep on setting changes
				game.setServerIP(serverIPField.getText());
    			game.setServerPort(serverPortField.getText());
   				game.setPlayerName(playerNameField.getText());
   				game.setGameName(gameNameField.getText());
   				//if no algorithm is selected in the list, do not attempt to start game
    			if (PursuitAlgorithmListPanel.getSelectedItem() == null && !game.getObserverMode()) 
    			{
    				print("Error: no algorithm selected!");
    				return;
    			}
    			//start game
    			game.start();
    		}
    		if (source.getText().equals("Disconnect")) 
    		{
    			source.setText("CONNECT!");
    			game.interrupt();
    		}
    		if (source.getText().equals("Add")) 
    		{
    			JFileChooser chooser = new JFileChooser(".");
				int returnVal = chooser.showOpenDialog(this);
				File file = new File("");
		
				if (returnVal == JFileChooser.APPROVE_OPTION) 
				{
					file = chooser.getSelectedFile();
					print("Checking " + file.getName() + "...");
					algorithmPanel.addAlgorithm(file, true);
				}
				else 
					print("Add command cancelled by user.");
    		}
    		if (source.getText().equals("Remove")) 
    			algorithmPanel.removeAlgorithm();
    	}
    }
    
    //handles check box interactions
    public void itemStateChanged(ItemEvent e) 
    {
    	Object source = e.getItemSelectable();
    	if (source == observerModeCheckBox) 
    	{	//observer mode
    		game.toggleObserverMode();
    		print("Observer Mode Toggled.");
    	}
    	if (source == logCheckBox) 
    	{	//logging mode
    		isLogging = !isLogging;
    		print("Logging Output Mode Toggled.");
    	}
    }
    
    public void focusGained(FocusEvent e) 
    	{}
    
    public void focusLost(FocusEvent e) 
    {
    	Object source = e.getSource();
    	//handles all setting field changes. Changes are saved when fields lose focus.
    	if (source == serverIPField && !serverIPField.getText().equals(game.getServerIP())) 
    	{
    		game.setServerIP(serverIPField.getText());
			print("New Server IP: " + game.getServerIP());
    	}
    	else if (source == serverPortField && !serverPortField.getText().equals(game.getServerPort())) 
    	{
    		game.setServerPort(serverPortField.getText());
			print("New Server Port: " + game.getServerPort());
    	}
    	else if (source == playerNameField && !playerNameField.getText().equals(game.getPlayerName())) 
    	{
    		game.setPlayerName(playerNameField.getText());
			print("New Player Name: " + game.getPlayerName());
    	}
    	else if (source == gameNameField && !gameNameField.getText().equals(game.getGameName())) 
    	{
    		game.setGameName(gameNameField.getText());
			print("New Game Name: " + game.getGameName());
    	}
    }

	//prints string plus its timestamp to console textarea
    public static final synchronized void print(String s) 
    {
		Calendar calendar = Calendar.getInstance();
		//modify timestamp for single digit cases: add 0 prefix
		String hour = Integer.toString(calendar.get(Calendar.HOUR));
		if (hour.length() == 1) 
			hour = "0" + hour;
		String minute = Integer.toString(calendar.get(Calendar.MINUTE));
		if (minute.length() == 1) 
			minute = "0" + minute;
		String second = Integer.toString(calendar.get(Calendar.SECOND));
		if (second.length() == 1) 
			second = "0" + second;
		String millisecond = Integer.toString(calendar.get(Calendar.MILLISECOND));
		while (millisecond.length() < 3) 
			millisecond = "0" + millisecond; //modify milliseconds for 1&2 digit cases
		
    	String timestamp = "[" + hour + ":" + minute + ":" + second + ":" + millisecond + "]";
    	console.append(timestamp + " " + s + "\n");
    	
    	//if logging is enabled, regurgitate into log file.
    	if (isLogging) 
    		log.append(timestamp + " " + s);
    	
    	//force caret (and view) to bottom of textarea
    	console.setCaretPosition(console.getDocument().getLength());
    }
    
    public final PursuitAlgorithmListPanel getAlgorithmListPanel() 
    	{	return algorithmPanel;	}
    public final PursuitGraphicsPanel2 getGraphicsPanel() 
    	{	return graphics_panel;	}
    public static final JScrollPane getGraphicsScrollpane() 
	{	return graphics_scrollpane;	}
    public final PursuitDebugger getDebugger() 
		{	return debug;	}
}